#include <iostream>
#include <memory>


#include "plc2llvm/ScopeSystem/ScopeManager.h"
#include "plc2llvm/TypeSystem/TypeMachine.h"
#include "plc2llvm/TypeSystem/Basic/BasicInclude.h"

using namespace std;
using namespace plcst;

// 类型检查

void printConvRes(shared_ptr<Type> src, shared_ptr<Type> dest){
    // dest = src
    auto sign = src->canConvertTo(dest);
    switch (sign)
    {
    case ConvertionSignal::IMPLICIT_CONVERSION:
        cout << src->toString() << " can convert to "<< dest->toString() << " through " << "IMPLICIT_CONVERSION\n";
        break;
    case ConvertionSignal::EXPLICIT_CONVERSION:
        cout << src->toString() << " can convert to "<< dest->toString() << " through " << "EXPLICIT_CONVERSION\n";
        break;
    default:
        cout << src->toString() << " can not convert to "<< dest->toString() << ".\n";
        break;
    }
}

void print2OpRes(shared_ptr<Type> src, shared_ptr<Type> another, ExpressionOperator op, std::string opstr = "unknow"){
    // type(src op another)
    auto dest = src->typeSynthesisForBinaryOperator(op, another);
    if(dest.signal == 2){
        cout << opstr << "(" << src->toString() << ", " << another->toString() << ") invalid.\n"; 
        return;
    }
    cout << opstr << "(" << src->toString() << ", " << another->toString() << ") -> " << dest.t->toString() << ", signal: " << dest.signal << "\n";

}

void print1OpRes(shared_ptr<Type> src, ExpressionOperator op, std::string opstr = "unknow") {
    auto dest = src->typeSynthesisForUnaryOperator(op);
    if(dest.signal == 2){
        cout << opstr << "(" << src->toString() << ") invalid.\n"; 
        return;
    }
    cout << opstr << "(" << src->toString()  << ") -> " << dest.t->toString() << ", signal: " << dest.signal << "\n";
}

std::string opToString(ExpressionOperator op){
    switch(op){
        case ExpressionOperator::POWER:
            return "POWER";
        case ExpressionOperator::MULTI: 
            return "MULTI";
        case ExpressionOperator::DIV: 
            return "DIV";
        case ExpressionOperator::MOD: 
            return "MOD";
        case ExpressionOperator::ADD: 
            return "ADD";
        case ExpressionOperator::MINUS: 
            return "MINUS";
        case ExpressionOperator::LESS: 
            return "LESS";
        case ExpressionOperator::GREATER: 
            return "GREATER";
        case ExpressionOperator::LESSEQUAL:
            return "LESSEQUAL";
        case ExpressionOperator::GREATEREQUAL: 
            return "GREATEREQUAL";
        case ExpressionOperator::EQUAL: 
            return "EQUAL";
        case ExpressionOperator::NOTEQUAL: 
            return "NOTEQUAL";
        case ExpressionOperator::AND: 
            return "AND";
        case ExpressionOperator::XOR: 
            return "XOR";
        case ExpressionOperator::OR: 
            return "OR";
        case ExpressionOperator::POSITIVE:
            return "POSITIVE";
        case ExpressionOperator::NEGATIVE:
            return "NEGATIVE";
        case ExpressionOperator::NOT:
            return "NOT";
        default:
            return "unknow";
    }
}

int main(){
    // TODO: add more basic types
    std::vector<shared_ptr<Type>> typelist = {
        make_shared<INTType>("INT", 100),
        make_shared<REALType>("REAL", 1000),
        make_shared<UINTType>("UINT", 10),
        make_shared<STRINGType>("STRING"),
        make_shared<TODType>("TOD")
    };

    std::vector<ExpressionOperator> op2operandlist = {
        ExpressionOperator::POWER, 
        ExpressionOperator::MULTI, 
        ExpressionOperator::DIV, 
        ExpressionOperator::MOD, 
        ExpressionOperator::ADD, 
        ExpressionOperator::MINUS, // **, *, /, %, +, -
        ExpressionOperator::LESS, 
        ExpressionOperator::GREATER, 
        ExpressionOperator::LESSEQUAL,
         ExpressionOperator::GREATEREQUAL, // >, <, <=, >=
        ExpressionOperator::EQUAL, 
        ExpressionOperator::NOTEQUAL, // ==, !=
        ExpressionOperator::AND, 
        ExpressionOperator::XOR, 
        ExpressionOperator::OR, //&, ^, |
    };

    std::vector<ExpressionOperator> op1operandlst = {
        ExpressionOperator::POSITIVE,
        ExpressionOperator::NEGATIVE,
        ExpressionOperator::NOT
    };

    // test for x = y
    for(auto src : typelist){
        for(auto dest : typelist){
            printConvRes(src, dest);
        }
    }

    // test for x op y
    for(auto src : typelist){
        for(auto dest : typelist){
            for(auto op : op2operandlist){
                print2OpRes(src, dest, op, opToString(op));
            }
        }
    }

        // test for op x
    for(auto src : typelist){
        for(auto dest : typelist){
            for(auto op : op1operandlst){
                print1OpRes(src, op, opToString(op));
            }
        }
    }
    return 0;
}